'''
Helper functions for base-10 decomposition
'''

import math

DIGITS = 10

def raw_encode(num):
    # verify that this is an integer
    assert isinstance(num, int), "%r is not a number" % num

    precision = len(str(num))
    results = []
    for i in range(precision-1, -1, -1):
        sig_digits = (num / (10 ** i))
        term = str(sig_digits).zfill(DIGITS)
        results.append(term)
    return results

def encode(token_name, num):
    '''
    Encode a number into a series of token tuples.  Floating numbers are
    rounded up.  See README.TXT for a discussion on term encoding.
    '''
    # verify that this is an integer
    terms = raw_encode(num)
    t_length = len(terms)
    results = ["n%02d_%s:%s" % (t_length - i, token_name, t) for i, t in enumerate(terms)]
    return results


def lessthan(token_name, num):
    '''
    Return a dictionary of token_name -> list of values that need to be used
    for a less than search.

    1000 ->
        [1000,
        900, 800, 700, 600, 600, 500, 400, 300, 200, 100,
        90, 80, 70, 60, 50, 40, 30, 20, 10,
        9, 8, 7, 6, 5, 4, 3, 2, 1]

    1649 ->
        1649, 1648, 1647, 1646, 1645, 1644, 1643, 1642, 1641, 1640,
        1630, 1620, 1610, 1600,
        1500, 1400, 1300, 1200, 1100, 1000,
        900, 800, 700, 600, 500, 400, 300, 200, 100,
        90, 80, 70, 60, 50, 40, 30, 20, 10,
        9, 8, 7, 6, 5, 4, 3, 2, 1

    So generally, there are 2 phases to less than decomposition.  We need to go from most precise to least precise to 
    capture all the number that are greater than N/(10**precision(N-1)). In the above example, that means:

            1649, 1648, 1647, 1646, 1645, 1644, 1643, 1642, 1641, 1640,
            1630, 1620, 1610, 1600,
            1500, 1400, 1300, 1200, 1100, 1000,

    Then we need to capture all numbers less than (N/(10**precision(N-1))) by encoding 1-9 for each precision level 
    of length(N) down to 1.  An example illustrates this better:

            900, 800, 700, 600, 500, 400, 300, 200, 100,
            90, 80, 70, 60, 50, 40, 30, 20, 10,
            9, 8, 7, 6, 5, 4, 3, 2, 1

    A better example has no instance of '1' in any digit of the number.  

    2594 ->
        2594, 2593, 2593, 2592, 2591, 2590,
        2580, 2570, 2560, 2550, 2540, 2530, 2520, 2510, 2500,
        2400, 2300, 2200, 2100, 2000,
        1000,
        900, 800, 700, 600, 500, 400, 300, 200, 100,
        90, 80, 70, 60, 50, 40, 30, 20, 10,
        9, 8, 7, 6, 5, 4, 3, 2, 1
    '''
    assert isinstance(num, int)

    results = {}
    precision = len(str(num))

    # Do phase 1 decomposition of n01 -> precision
    for i in range(1, precision+1):
        lower_bound = int(math.floor((num*1.0) / (10 ** i)) * 10)
        upper_bound = int(math.floor((num*1.0) / (10 ** (i-1))))
        section = "n%02d_%s" % (i, token_name)
        for term in range(upper_bound, lower_bound -1, -1):
            if term <> 0:
                last_term = term
                last_precision = i
                term = str(term).zfill(DIGITS)
                results.setdefault(section, []).append(term)


    for i in range(last_precision - 2, -1, -1):
        section = "n%02d_%s" % (i+1, token_name)
        for term in range(9, 0, -1):
            last_term = term
            term = str(term).zfill(DIGITS)
            results.setdefault(section, []).append(term)
    return results

def greaterthan(token_name, num):
    '''
    Return a dictionary of token_name -> list of values that need to be used
    for a less than search.
    '''
    assert isinstance(num, int)

    results = {}
    precision = len(str(num))
    for i in range(10, precision+1, -1):
        # Force all the bigger numbers to be included
        results['n%02d_%s' % (i, token_name)] = [
                    '0000000009', '0000000008', '0000000007', '0000000006',
                    '0000000005', '0000000004', '0000000003', '0000000002',
                    '0000000001']

    for i in range(precision, -1, -1):
        results["n%02d_%s" % (i+1, token_name)] = []

        bottom = int(num / 10 ** i) + 1
        last_digit = int(str(bottom)[-1])
        if last_digit <> 0:
            top    = int(str(bottom)[:-1] + '9')
            if i == 0:
                bottom -= 1

            for lt_term in range(bottom, top+1):
                if lt_term <> 0:
                    results["n%02d_%s" % (i+1, token_name)].append(str(lt_term).zfill(DIGITS))
    return results

def flatten_range(term_dict):
    '''
    Take a range dictionary from lessthan or greaterthan and flatten it into a
    list of terms for matching
    '''
    results = []
    for key, values in term_dict.items():
        if values:
            for v in values:
                results.append("%s:%s" % (key, v))
    return results


